#include <iostream>
#include "opencv2/opencv.hpp"
#include <ctime>
#include <cstdio>
#include <cstdlib>
#include <string>
#include <vector>


#include <stdio.h>  
#include <stdlib.h>     
#include <string.h>  
#include <errno.h>  
#include <sys/socket.h>  
#include <arpa/inet.h>  
#include <netinet/in.h>  
#include <sys/types.h>  
#include <unistd.h> 
#include <netdb.h>
#include <sys/stat.h>
#include <fcntl.h>


#include <iostream>
#include <pthread.h>
#include <unistd.h>

#include <iostream>
#include <string>
#include <sys/stat.h>
#include <unistd.h>


/*
修改记录
348    178    111   81   


*/


#define  PORT      8888
#define  MSGLEN    1024


#define MAIN_WINDOW "实时人脸识别系统"
#define YUNET_ONNX_PATH "./models/yunet.onnx"
#define FACE_RECOGNIZER_FAST_ONNX_PATH "./models/face_recognizer_fast.onnx"
#define FACES_PATH "./sign-faces/"	// 表示文件夹路径记得带上末尾/
#define IMAGE_PATH "./sign-faces/" // 记得文件夹位置后面加上'/'
#define PICTURE_PATH "./glass.jpg"




using namespace std;
using namespace cv;

Mat faces_feature[500];
char face_name[500][100];	// 定义全局变量表示人名
int face_cnt = 0;
int send_flag = 0;//0:识别   1：注册
int detect_flag = 0;//是否进行识别
int detect_finish = 0,detect_results = 0;


void face_register(void);
string face_recognize(Mat face_img, Mat face_box);
int tcp(void);
bool moveFiles(const std::string& sourceDir, const std::string& destDir);

void* threadFunction1(void* arg) {
    int thread_id1 = *(int*)arg;
    std::cout << "Thread " << thread_id1 << " is running" << std::endl;

    tcp();

    pthread_exit(NULL);
}

void* threadFunction2(void* arg) {
    int thread_id2 = *(int*)arg;
    std::cout << "Thread " << thread_id2 << " is running" << std::endl;

    while(1)
    {
        char input;

        //根据键盘输入值，进行一系列对应的操作
        //d:删除人脸，l：查看人脸列表
        // cin>>input;     //std::cout << "Enter a character (a, b, or c): ";
        // std::cin >> input;
        std::cout << "Enter the action" << std::endl;
        std::cin >> std::noskipws >> input;
        getchar();

        switch (input) {
            case 'd':
                {
                    std::cout << "Enter the name you want to delete" << std::endl;
                    // 执行操作 删除人脸
                    string input_name;
                    cin >> input_name;
                    getchar();
                    std::string command = "rm ./sign-faces/" + input_name + ".jpg"; //根据输入的名字进行保存脸部图片
                    system(command.c_str());
                    face_register(); //删除后对其他重新注册
                    break;
                }


            case 'l':
                {
                    std::cout << "You entered 'b'. Performing operation B." << std::endl;

                    // 执行操作 查看人脸信息
                    face_register(); //直接注册，会打印出人脸信息
                    break;   
                }

            // case 'c':
            //     std::cout << "You entered 'c'. Performing operation C." << std::endl;
            //     // 执行操作 C
            //     break;
            default:
                {
                    std::cout << "Invalid input. Please enter 'a', 'b', or 'c'." << std::endl;
                    break;
                }
                // 清空输入缓冲区
            // std::cin.ignore(std::numeric_limits<std::streamsize>::max(), ' ');
        }        
    }
    pthread_exit(NULL);
}

Ptr<FaceDetectorYN> fd = FaceDetectorYN::create(YUNET_ONNX_PATH, "", Size(320, 320));	// 通过人脸模型找到人脸区域
Ptr<FaceRecognizerSF> fr = FaceRecognizerSF::create(FACE_RECOGNIZER_FAST_ONNX_PATH, "");	// 加载模型，构建深度神经网络，进行人脸数据特征分析

int main() {
	Mat img, faces;
	int key, i, face_x, face_y, face_w, face_h;
	string name;
	int is_me = 0, welcome = 0;
	time_t t1;

	char image_name[100];
	int photo_cnt = 1;
	string image_path;

	int m_volume = 50, music_flag = 0;

    pthread_t thread1;  //tcp进程
    int thread_id1 = 1;
    int rc1 = pthread_create(&thread1, NULL, threadFunction1, (void*)&thread_id1);
    if (rc1) {
        std::cerr << "Error: Unable to create thread, return code " << rc1 << std::endl;
        return -1;
    }


    pthread_t thread2;  //命令进程
    int thread_id2 = 2;
    int rc2 = pthread_create(&thread2, NULL, threadFunction2, (void*)&thread_id2);
    if (rc2) {
        std::cerr << "Error: Unable to create thread, return code " << rc2 << std::endl;
        return -1;
    }


    face_register();
    std::remove("face.jpg");


    printf("face_register success\n");
    while(1)
    {
        if(detect_flag) //只发送一张图片，则进行识别
        {
            detect_flag = 0;
            img = cv::imread("./face.jpg");

            fd->setInputSize(img.size());
            fd->detect(img, faces);

            is_me = 0;

            for(i = 0; i < faces.rows; i++)
            {
                name = face_recognize(img, faces.row(i));

                face_x = faces.at<float>(i, 0);
                face_y = faces.at<float>(i, 1);
                face_w = faces.at<float>(i, 2);
                face_h = faces.at<float>(i, 3);

                // if(name == "zhang")	is_me = 1;
                // if(name == "MaYun")	is_me = 2;
                // if(name == "face1")	is_me = 3;

                rectangle(img, Rect(face_x, face_y, face_w, face_h), Scalar(200, 0, 0), 2);
            
                putText(img, name, Point(face_x, face_y - 5), FONT_HERSHEY_COMPLEX, 1.0, Scalar(200, 200, 0), 2);
                }
            if (name.substr(0, 4) == "face") //判断是否识别成功
            {
                cout << "识别成功" << endl;
                detect_results = 1;
            }
            else
            {
                cout << "识别失败" << endl;
                detect_results = 0;
            }
            detect_finish = 1;
            std::remove("face.jpg");
        }

    }
    return 0;
}

// 人脸信息注册
void face_register(void)
{
    	Mat img, faces, aligned_img, face_feature;
	vector<string> path;
	const char* faces_dir = FACES_PATH;
	const char* q;
	int i;

    //clear face data
    face_cnt = 0;


	glob(FACES_PATH, path);	// 将文件夹中的所有文件信息读取到顺序表

	for(auto p: path)
	{
		img = imread(p);

		// 大尺寸图片缩小（等比缩放）
		if(img.cols > 1024)
		{
			resize(img, img, Size(), 1024.0/img.cols, 1024.0/img.cols);
		}

		fd->setInputSize(img.size());
		fd->detect(img, faces);
		
		if(faces.rows == 0)
		{
            remove(p.c_str());
			cerr << p << ": 未检测到人脸" << endl;
			continue;
		}
		if(faces.rows > 1)
		{
            remove(p.c_str());
			cerr << p << ": 检测到多个人脸信息，注册失败" << endl << endl;
			continue;
		}


		fr->alignCrop(img, faces.row(0), aligned_img);	// 将录入的人脸图像进行对齐和裁剪，结果存放在 aligend_img 中
		fr->feature(aligned_img, face_feature);			// 计算人脸特征数据，并存放在 face_feature 中

		faces_feature[face_cnt] = face_feature.clone();	// 把人脸特征存放到全局变量中

		q = p.c_str() + strlen(faces_dir);
		
		for(i = 0; *(q + i) != '.'; i++)
			face_name[face_cnt][i] = *(q + i);
		
		face_name[face_cnt][i] = '\0';

		cout << face_name[face_cnt] << endl;
		face_cnt++;
	}
		cout << "注册成功! 人脸数： " << face_cnt << endl;
}


// 人脸识别
string face_recognize(Mat face_img, Mat face_box)
{
	Mat aligned_img, face_feature;
	double cosine_score;
	int i;

	fr->alignCrop(face_img, face_box.row(0), aligned_img);	// 将录入的人脸图像进行对齐和裁剪，结果存放在 aligend_img 中
	fr->feature(aligned_img, face_feature);			// 计算人脸特征数据，并存放在 face_feature 中
	
	for(i = 0; i < face_cnt; i++)
	{
		cosine_score = fr->match(face_feature, faces_feature[i]); // 计算两个特征向量的相似度，采用余弦距离的方法，数值越大相似度越高

		if(cosine_score > 0.300)
		{
			return face_name[i];
		}
	}
	return "?";
}




int tcp(void)
{
    int severFd, clientFd;
    int fp,flags;
    socklen_t addrlen;
    struct sockaddr_in severAddr, clientAddr;
    char recvBuff[MSGLEN];
    // char filename[100];

    char filename[] = "face.jpg";
    int recv_len;
    int rv_flag = 0;

    if((severFd = socket(AF_INET, SOCK_STREAM, 0)) == -1){
        perror("sockst() error");
        exit(-1);
    }

    severAddr.sin_family = AF_INET;
    severAddr.sin_port   = htons(PORT);
    severAddr.sin_addr.s_addr = htons(INADDR_ANY);
    bzero(&severAddr.sin_zero, 8);

    if(bind(severFd, (struct sockaddr*)&severAddr, sizeof(struct sockaddr)) == -1) {
        perror("bind() error");
        exit(-1);
    }

    if(listen(severFd, 1) == -1) {
        perror("listen() error");
        exit(-1);
    }
    
    addrlen =sizeof(struct sockaddr);
    while(1) {
        flags = 0;
        if((clientFd = accept(severFd, (struct sockaddr*)&clientAddr, &addrlen)) == -1){
            perror("accept() error");
            exit(-1);
        }
        printf("recv file fome ip:%s  port: %d\n", inet_ntoa(clientAddr.sin_addr), ntohs(clientAddr.sin_port));

        if(recv(clientFd, &rv_flag, sizeof(int), 0) < 0){   //接收标志位
            perror("recv filename error");
            break; //less
        }
        printf("The rv_flag is: %d\n", rv_flag);   


        printf("The filename is: %s\n", filename);




        if(rv_flag == 1) {             //(strcmp(filename, "face") == 0) &&
            printf("enter  sign-face \n");
  
            fp = open(filename, O_RDWR | O_CREAT, 777);                  //接收文件
            while((recv_len = recv(clientFd, recvBuff, MSGLEN, 0)) > 0) {
                flags++;
                if(flags == 1) {
                    printf("recv file start");
                }
                else {
                    printf(".");
                }
                if(write(fp, recvBuff, recv_len)) {
                    bzero(&recvBuff, MSGLEN);    
                }else {
                    perror("write() error");
                    break;
                }
            } 
            if(flags == 0) 
                perror("recv() error");
            if(flags > 0){

                off_t file_size = lseek(fp, 0, SEEK_END);
                lseek(fp, 0, SEEK_SET);

                // 创建一个缓冲区来存储文件内容
                char* buffer = new char[file_size];
                // 读取文件内容到缓冲区
                read(fp, buffer, file_size);
                // 将缓冲区转换为OpenCV的Mat对象
                cv::Mat img(1, file_size, CV_8UC1, buffer);
                // 将Mat对象保存为JPEG图片
                cv::imwrite("./face.jpg", img);
                //释放资源并关闭文件
                delete[] buffer;

                //根据键盘输入值，判断是否进行人脸注册??????????????
                std::string command = "mv face.jpg ./sign-faces/face1.jpg"; //根据输入的名字进行保存脸部图片
                int status = system(command.c_str());
                face_register();
/*
                string input;
                cout << "是否进行人脸注册？(y/n): ";
                cin >> input;

                if (input == "y") {
                    cout << "进行人脸注册" << endl;
                    // 在这里添加进行人脸注册的代码

                    cout << "输入名字：";
                    cin >> input;

                    // std::string command = "mv face* ./sign-faces/face1";  //mv ./face ./faces/

                    std::string command = "mv face* ./sign-faces/" + input; //根据输入的名字进行保存脸部图片
                    int status = system(command.c_str());
                    
                    // face_register();
                } else if (input == "n") {
                    cout << "不进行人脸注册" << endl;
                } else {
                    cout << "输入无效" << endl;
                }
*/
                close(fp);
                close(clientFd);
            }
        }

        //否则进行检测
        else if(rv_flag == 0){
            printf("enter  sign-detc \n");
            fp = open(filename, O_RDWR | O_CREAT, 777);
            while((recv_len = recv(clientFd, recvBuff, MSGLEN, 0)) > 0) {
                // printf("enter  sign-detc \n");
                flags++;
                if(flags == 1) {
                    printf("recv file start");
                }
                else {
                    printf(".");
                }
                if(write(fp, recvBuff, recv_len)) {
                    bzero(&recvBuff, MSGLEN);    
                }else {
                    perror("write() error");
                    break;
                }
            } 
            if(flags == 0) 
                perror("recv() error");
            if(flags > 0){

                off_t file_size = lseek(fp, 0, SEEK_END);
                lseek(fp, 0, SEEK_SET);

                // 创建一个缓冲区来存储文件内容
                char* buffer = new char[file_size];
                // 读取文件内容到缓冲区
                read(fp, buffer, file_size);
                // 将缓冲区转换为OpenCV的Mat对象
                cv::Mat img(1, file_size, CV_8UC1, buffer);
                // 将Mat对象保存为JPEG图片
                cv::imwrite("./face.jpg", img);
                //释放资源并关闭文件
                delete[] buffer;
                close(fp);
                printf("\nrecv success\n");

                detect_flag = 1;  //开启检测开关

                if((clientFd = accept(severFd, (struct sockaddr*)&clientAddr, &addrlen)) == -1){   //等待客户端重新连接
                    perror("accept() error");
                    exit(-1);
                }
                printf("recv file fome ip:%s  port: %d\n", inet_ntoa(clientAddr.sin_addr), ntohs(clientAddr.sin_port));
                //根据检测结果发送不同的数据给客户端
                while(!detect_finish); //等待检测完成
                if(send(clientFd, &detect_results, sizeof(int), 0) < 0){
                    perror("send() error");
                    exit(-1);
                }
                printf("send success\n");
                close(clientFd); //断开之前，先将检测结果发给客户端
                
            }
        }

    }

    close(severFd);
    return 0;
}



bool moveFiles(const std::string& sourceDir, const std::string& destDir) {
    struct stat st;
    
    // if (stat(destDir.c_str(), &st) == -1) {
    //     // 创建目标文件夹
    //     int success = mkdir(destDir.c_str(), 0777);
    //     if (success == -1) {
    //         std::cerr << "Failed to create destination directory" << std::endl;
    //         return false;
    //     }
    // }
    
    // 执行系统调用，将源文件夹下的文件移动到目标文件夹中
    std::string command = "mv " + sourceDir  + destDir;  //mv ./face ./faces/
    int status = system(command.c_str());
    
    if (status == -1) {
        std::cerr << "Failed to move files" << std::endl;
        return false;
    }
    
    return true;
}
